using System.Diagnostics.CodeAnalysis;
using System.Linq.Expressions;
using Microsoft.EntityFrameworkCore;
using ZeroSum.Domain.Entities;
using ZeroSum.Domain.Repositories;
using ZeroSum.Domain.UnitOfWork;
using NotSupportedException = ZeroSum.Exceptions.NotSupportedException;

namespace ZeroSum.Extend.EFCore.MySql.EntityFrameworks;

/// <summary>
///     EFCore的仓储实现
/// </summary>
/// <typeparam name="TDbContext"></typeparam>
public class EfCoreRepository<TDbContext> : IRepository, IEfCoreRepository
    where TDbContext : DbContext
{
    private readonly TDbContext _dbContext;
    private readonly IUnitOfWork _unitOfWork;

    public EfCoreRepository(TDbContext dbContext, IUnitOfWork unitOfWork)
    {
        _dbContext = dbContext;
        _unitOfWork = unitOfWork;
    }

    public DbSet<TEntity> Entities<TEntity>() where TEntity : class, IEntity
    {
        return _dbContext.Set<TEntity>();
    }

    public virtual TEntity Add<TEntity>([NotNull] TEntity entity) where TEntity : class, IEntity
    {
        return _dbContext.Add(entity).Entity;
    }

    public virtual async Task<TEntity> AddAsync<TEntity>([NotNull] TEntity entity) where TEntity : class, IEntity
    {
        return Add(entity);
    }

    public virtual void AddRange<TEntity>(IEnumerable<TEntity> entities) where TEntity : class, IEntity
    {
        Entities<TEntity>().AddRange(entities);
    }

    public virtual async Task AddRangeAsync<TEntity>(IEnumerable<TEntity> entities) where TEntity : class, IEntity
    {
        await Entities<TEntity>().AddRangeAsync(entities);
    }

    public virtual TEntity Update<TEntity>([NotNull] TEntity entity) where TEntity : class, IEntity
    {
        _dbContext.Attach(entity);
        return _dbContext.Update(entity).Entity;
    }

    public virtual async Task<TEntity> UpdateAsync<TEntity>([NotNull] TEntity entity) where TEntity : class, IEntity
    {
        return Update(entity);
    }

    public virtual void UpdateRange<TEntity>(IEnumerable<TEntity> entities) where TEntity : class, IEntity
    {
        Entities<TEntity>().UpdateRange(entities);
    }

    public virtual async Task UpdateRangeAsync<TEntity>(IEnumerable<TEntity> entities) where TEntity : class, IEntity
    {
        UpdateRange(entities);
    }

    public virtual TEntity Get<TEntity>(IQuery<TEntity> query) where TEntity : class, IEntity
    {
        if (query is QueryByEntity<TEntity> entityQuery) return entityQuery.Query(Query<TEntity>()).First();

        throw NotSupportedException.Of($"暂不支持类型为的[{query.GetType()}]Query.");
    }

    public virtual async Task<TEntity> GetAsync<TEntity>(IQuery<TEntity> query) where TEntity : class, IEntity
    {
        if (query is QueryByEntity<TEntity> entityQuery) return await entityQuery.Query(Query<TEntity>()).FirstAsync();

        throw NotSupportedException.Of($"暂不支持类型为的[{query.GetType()}]Query.");
    }

    public virtual IList<TEntity> GetList<TEntity>(IQuery<TEntity> query) where TEntity : class, IEntity
    {
        if (query is QueryByEntity<TEntity> entityQuery) return entityQuery.Query(Query<TEntity>()).ToList();

        throw NotSupportedException.Of($"暂不支持类型为的[{query.GetType()}]Query.");
    }

    public virtual async Task<IList<TEntity>> GetListAsync<TEntity>(IQuery<TEntity> query)
        where TEntity : class, IEntity
    {
        if (query is QueryByEntity<TEntity> entityQuery) return await entityQuery.Query(Query<TEntity>()).ToListAsync();

        throw NotSupportedException.Of($"暂不支持类型为的[{query.GetType()}]Query.");
    }

    public virtual TEntity Get<TEntity>([NotNull] Expression<Func<TEntity, bool>> predicate)
        where TEntity : class, IEntity
    {
        return Entities<TEntity>().First(predicate);
    }

    public virtual async Task<TEntity> GetAsync<TEntity>([NotNull] Expression<Func<TEntity, bool>> predicate)
        where TEntity : class, IEntity
    {
        return await Entities<TEntity>().FirstAsync(predicate);
    }

    public virtual IList<TEntity> GetList<TEntity>([NotNull] Expression<Func<TEntity, bool>> predicate)
        where TEntity : class, IEntity
    {
        return Entities<TEntity>().Where(predicate).ToList();
    }

    public virtual async Task<IList<TEntity>> GetListAsync<TEntity>([NotNull] Expression<Func<TEntity, bool>> predicate)
        where TEntity : class, IEntity
    {
        return await Entities<TEntity>().Where(predicate).ToListAsync();
    }

    public virtual IList<TEntity> GetPage<TEntity>(int pageNumber, int pageSize,
        Expression<Func<TEntity, object>> sorting)
        where TEntity : class, IEntity
    {
        return Query<TEntity>().OrderBy(sorting)
            .Skip((pageNumber - 1) * pageSize)
            .Take(pageSize)
            .ToList();
    }

    public virtual async Task<IList<TEntity>> GetPageAsync<TEntity>(int pageNumber, int pageSize,
        Expression<Func<TEntity, object>> sorting) where TEntity : class, IEntity
    {
        return await Query<TEntity>().OrderBy(sorting)
            .Skip((pageNumber - 1) * pageSize)
            .Take(pageSize)
            .ToListAsync();
    }

    public virtual void Remove<TEntity>([NotNull] TEntity entity) where TEntity : class, IEntity
    {
        Entities<TEntity>().Remove(entity);
    }

    public virtual async Task RemoveAsync<TEntity>([NotNull] TEntity entity) where TEntity : class, IEntity
    {
        Remove(entity);
    }

    public virtual void Remove<TEntity>([NotNull] Expression<Func<TEntity, bool>> predicate)
        where TEntity : class, IEntity
    {
        var entity = Get(predicate);
        Entities<TEntity>().Remove(entity);
    }

    public virtual async Task RemoveAsync<TEntity>([NotNull] Expression<Func<TEntity, bool>> predicate)
        where TEntity : class, IEntity
    {
        var entity = await GetAsync(predicate);
        Entities<TEntity>().Remove(entity);
    }

    public virtual void RemoveRange<TEntity>(IEnumerable<TEntity> entities) where TEntity : class, IEntity
    {
        Entities<TEntity>().RemoveRange(entities);
    }

    public virtual void RemoveRange<TEntity>() where TEntity : class, IEntity
    {
        Entities<TEntity>().RemoveRange();
    }

    public virtual async Task RemoveRangeAsync<TEntity>(IEnumerable<TEntity> entities) where TEntity : class, IEntity
    {
        RemoveRange(entities);
    }

    public virtual void RemoveRange<TEntity>(Expression<Func<TEntity, bool>> predicate) where TEntity : class, IEntity
    {
        var entities = GetList(predicate);
        RemoveRange(entities);
    }

    public virtual async Task RemoveRangeAsync<TEntity>(Expression<Func<TEntity, bool>> predicate)
        where TEntity : class, IEntity
    {
        var entities = await GetListAsync(predicate);
        await RemoveRangeAsync(entities);
    }

    public virtual void Remove<TEntity>(IQuery<TEntity> query) where TEntity : class, IEntity
    {
        var entity = Get(query);
        Remove(entity);
    }

    public virtual async Task RemoveAsync<TEntity>(IQuery<TEntity> query) where TEntity : class, IEntity
    {
        var entity = await GetAsync(query);
        await RemoveAsync(entity);
    }

    public virtual void RemoveRange<TEntity>(IQuery<TEntity> query) where TEntity : class, IEntity
    {
        var entities = GetList(query);
        RemoveRange(entities);
    }

    public virtual async Task RemoveRangeAsync<TEntity>(IQuery<TEntity> query) where TEntity : class, IEntity
    {
        var entities = await GetListAsync(query);
        await RemoveRangeAsync(entities);
    }

    public virtual int Count<TEntity>() where TEntity : class, IEntity
    {
        return Entities<TEntity>().Count();
    }

    public virtual async Task<int> CountAsync<TEntity>() where TEntity : class, IEntity
    {
        return await Entities<TEntity>().CountAsync();
    }

    public virtual bool Any<TEntity>(Expression<Func<TEntity, bool>> predicate) where TEntity : class, IEntity
    {
        return Entities<TEntity>().Any(predicate);
    }

    public virtual async Task<bool> AnyAsync<TEntity>(Expression<Func<TEntity, bool>> predicate)
        where TEntity : class, IEntity
    {
        return await Entities<TEntity>().AnyAsync(predicate);
    }

    public virtual bool Existed<TEntity>(IQuery<TEntity> query) where TEntity : class, IEntity
    {
        if (query is QueryByEntity<TEntity> entityQuery) return entityQuery.Query(Query<TEntity>()).Any();

        throw NotSupportedException.Of($"暂不支持类型为的[{query.GetType()}]Query.");
    }

    public virtual async Task<bool> ExistedAsync<TEntity>(IQuery<TEntity> query) where TEntity : class, IEntity
    {
        if (query is QueryByEntity<TEntity> entityQuery) return await entityQuery.Query(Query<TEntity>()).AnyAsync();

        throw NotSupportedException.Of($"暂不支持类型为的[{query.GetType()}]Query.");
    }

    public virtual int Count<TEntity>(IQuery<TEntity> query) where TEntity : class, IEntity
    {
        if (query is QueryByEntity<TEntity> entityQuery) return entityQuery.Query(Query<TEntity>()).Count();

        throw NotSupportedException.Of($"暂不支持类型为的[{query.GetType()}]Query.");
    }

    public virtual async Task<int> CountAsync<TEntity>(IQuery<TEntity> query) where TEntity : class, IEntity
    {
        if (query is QueryByEntity<TEntity> entityQuery) return await entityQuery.Query(Query<TEntity>()).CountAsync();

        throw NotSupportedException.Of($"暂不支持类型为的[{query.GetType()}]Query.");
    }

    public virtual void SaveChanges()
    {
        _unitOfWork.SaveChanged();
    }

    public virtual async Task SaveChangesAsync()
    {
        await _unitOfWork.SaveChangesAsync();
    }

    protected IQueryable<TEntity> Query<TEntity>() where TEntity : class, IEntity
    {
        return Entities<TEntity>().AsQueryable();
    }
}